package edebe.flyinginstrument.entity;

import edebe.flyinginstrument.item.IFlyingInstrumentItem;
import edebe.flyinginstrument.key.ModKeys;
import com.google.common.base.Optional;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.*;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemElytra;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.UUID;

@ParametersAreNonnullByDefault
public class EntityFlySword extends EntityLiving implements IFlyingInstrumentEntity {
    private static final String NBT_KEY_ITEMSTACK = "ItemStack";
    private static final String NBT_KEY_OWNER_UUID = "OwnerUniqueID";

    private static final DataParameter<ItemStack> SWORD_ITEM_STACK = EntityDataManager.createKey(EntityFlySword.class, DataSerializers.ITEM_STACK);
    private static final DataParameter<Byte> CONTROL_STATE = EntityDataManager.createKey(EntityFlySword.class, DataSerializers.BYTE);
    protected static final DataParameter<Optional<UUID>> OWNER_UNIQUE_ID = EntityDataManager.createKey(EntityFlySword.class, DataSerializers.OPTIONAL_UNIQUE_ID);

    protected double mountedYOffset;
    protected boolean shouldRiderSit;

    public EntityFlySword(World worldIn) {
        super(worldIn);
        this.setEntityInvulnerable(true);
    }

    public EntityFlySword(World worldIn, ItemStack stackIn, EntityPlayer playerIn) {
        this(worldIn);
        this.setItemStack(stackIn);
        this.setOwnerID(playerIn.getUniqueID());
    }
    protected void applyEntityAttributes()
    {
        super.applyEntityAttributes();
        this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(6.0D);
    }
    @Override
    protected void entityInit() {
        super.entityInit();
        this.dataManager.register(SWORD_ITEM_STACK, ItemStack.EMPTY);
        this.dataManager.register(CONTROL_STATE, (byte) 0);
        this.dataManager.register(OWNER_UNIQUE_ID, Optional.absent());
    }

    @Override
    public void readEntityFromNBT(NBTTagCompound compound) {
        super.readEntityFromNBT(compound);
        if (compound.hasKey(NBT_KEY_ITEMSTACK, 10)) this.setItemStack(new ItemStack(compound.getCompoundTag(NBT_KEY_ITEMSTACK)));
        if (compound.hasKey(NBT_KEY_OWNER_UUID)) this.setOwnerID(compound.getUniqueId(NBT_KEY_OWNER_UUID));
    }

    @Override
    public void writeEntityToNBT(NBTTagCompound compound) {
        super.writeEntityToNBT(compound);
        if (!this.getItemStack().isEmpty()) compound.setTag(NBT_KEY_ITEMSTACK, this.getItemStack().writeToNBT(new NBTTagCompound()));
        if (this.getOwnerID() != null) compound.setUniqueId(NBT_KEY_OWNER_UUID, this.getOwnerID());
    }

    @Override
    public double getMountedYOffset() {
        return this.mountedYOffset;
    }

    @Override
    public boolean shouldRiderSit() {
        return this.shouldRiderSit;
    }

    @Nullable
    @Override
    public Entity getControllingPassenger() {
        List<Entity> list = this.getPassengers();
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public void updatePassenger(@Nonnull Entity passenger) {
        super.updatePassenger(passenger);
        if (passenger instanceof EntityLiving) {
            EntityLiving entityliving = (EntityLiving) passenger;
            //this.renderYawOffset = entityliving.renderYawOffset;
            entityliving.dismountRidingEntity();
        }
    }

    @Override
    protected void removePassenger(Entity entity) {
        super.removePassenger(entity);
        if (entity.getUniqueID().equals(this.getOwnerID())) this.putAwaySword((EntityPlayer) entity);
    }

    //@Override
    public boolean canBeSteered() {
        return this.getControllingPassenger() instanceof EntityLivingBase;
    }

    @Override
    public void fall(float distance, float damageMultiplier) {
        damageMultiplier = 0;
        super.fall(distance, damageMultiplier);
    }

    @Override
    public boolean attackEntityFrom(DamageSource source, float amount) {
        EntityPlayer player = this.getControllingPlayer();
        if (player != null || source.getTrueSource() == null) return false;
        if (this.isOwner(source.getTrueSource())) this.putAwaySword((EntityPlayer) source.getTrueSource());
        return false;
    }



    private void updateElytra()
    {
        boolean flag = this.getFlag(7);

        if (flag && !this.onGround && !this.isRiding())
        {
            ItemStack itemstack = this.getItemStackFromSlot(EntityEquipmentSlot.CHEST);

            if (itemstack.getItem() == Items.ELYTRA && ItemElytra.isUsable(itemstack))
            {
                flag = true;

                if (!this.world.isRemote && (this.ticksElytraFlying + 1) % 20 == 0)
                {
                    itemstack.damageItem(1, this);
                }
            }
            else
            {
                flag = false;
            }
        }
        else
        {
            flag = false;
        }

        if (!this.world.isRemote)
        {
            this.setFlag(7, flag);
        }
    }


    @Override
    public void onUpdate() {


        this.world.profiler.endSection();
        this.world.profiler.startSection("travel");
        this.moveStrafing *= 0.98F;
        this.moveForward *= 0.98F;
        this.randomYawVelocity *= 0.9F;
        this.updateElytra();
        this.travel(this.moveStrafing, this.moveVertical, this.moveForward);




        Item item = this.getItemStack().getItem();
        if (item instanceof IFlyingInstrumentItem) {
            IFlyingInstrumentItem flyingInstrument = (IFlyingInstrumentItem) item;
            this.setSize(flyingInstrument.getWidth(), flyingInstrument.getHeight());
            this.mountedYOffset = flyingInstrument.getMountedYOffset();
            this.stepHeight = flyingInstrument.getStepHeight();
            this.shouldRiderSit = flyingInstrument.shouldRiderSit();
        }
        EntityPlayer player = this.getControllingPlayer();
        if (!this.world.isRemote && player != null && player.isSneaking()) {
            this.putAwaySword(player);
            return;
        }
        if (!this.world.isRemote && player == null ) {
            this.setDead();
            return;
        }
        super.onUpdate();
        if (this.world.isRemote) this.updateClientControls();
        if (player != null) {
            NBTTagCompound data = player.getEntityData();
            if (data.hasKey("Power") && data.getDouble("Power") > 0) {
                if (this.getItemStack().getItem() instanceof IFlyingInstrumentItem) {
                    data.setBoolean("FaQiFly", true);
                    if (data.hasKey("JingJieNum")) {
                        data.setDouble("FlySowrdTime", data.getDouble("FlySowrdTime") + 1);
                        if (data.getDouble("FlySowrdTime") >= 20) {
                            data.setDouble("FlySowrdTime", 0);
                            if (data.getDouble("JingJieNum") >= 1 && data.getDouble("JingJieNum") <= 2)
                                data.setDouble("Power", data.getDouble("Power") - 3);
                            else data.setDouble("Power", data.getDouble("Power") - 6);
                        }
                    }
                }
            } else {
                data.setBoolean("FaQiFly", false);
                this.putAwaySword(player);
            }
        }
    }

    @Override
    protected void playStepSound(BlockPos pos, Block blockIn) { }

    @Override
    public void travel(float strafe, float vertical, float forward) {
        EntityPlayer player;
        if (this.isBeingRidden() && (player = this.getControllingPlayer()) != null) {
            if (this.down()) this.motionY -= 0.03F;
            if (this.up()) this.motionY += 0.03F;

            this.rotationYaw = player.rotationYaw;
            this.prevRotationYaw = this.rotationYaw;
            this.rotationPitch = player.rotationPitch * 0.5F;
            this.setRotation(this.rotationYaw, this.rotationPitch);
            this.renderYawOffset = this.rotationYaw;
            this.rotationYawHead = this.renderYawOffset;
            strafe = player.moveStrafing * 0.5F;
            forward = player.moveForward;

            if (forward <= 0.0F) forward *= 0.25F;

            this.jumpMovementFactor = this.getAIMoveSpeed() * 0.1F;

            if (this.canPassengerSteer()) {
                this.setAIMoveSpeed(player.getAIMoveSpeed() * 1F);
                this.doTravel(strafe, vertical, forward);
            } else {
                this.motionX = 0.0D;
                this.motionY = 0.0D;
                this.motionZ = 0.0D;
            }

            this.prevLimbSwingAmount = this.limbSwingAmount;
            double d1 = this.posX - this.prevPosX;
            double d0 = this.posZ - this.prevPosZ;
            float f2 = MathHelper.sqrt(d1 * d1 + d0 * d0) * 4.0F;

            if (f2 > 1.0F) f2 = 1.0F;

            this.limbSwingAmount += (f2 - this.limbSwingAmount) * 0.4F;
            this.limbSwing += this.limbSwingAmount;

        } else {
            this.motionX = 0.0D;
            this.motionY = 0.0D;
            this.motionZ = 0.0D;
            this.jumpMovementFactor = 0.02F;
            this.doTravel(strafe, vertical, forward);
        }
    }

    private void doTravel(float strafe, float vertical, float forward) {
        this.moveRelative(strafe, vertical, forward, jumpMovementFactor);
        this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
    }

    protected void setItemStack(ItemStack itemStack) {
        this.dataManager.set(SWORD_ITEM_STACK, itemStack);
    }

    public ItemStack getItemStack() {
        return this.dataManager.get(SWORD_ITEM_STACK);
    }

    public UUID getOwnerID() {
        return this.dataManager.get(OWNER_UNIQUE_ID).orNull();
    }

    @SuppressWarnings("all")
    protected void setOwnerID(@Nullable UUID uniqueID) {
        this.dataManager.set(OWNER_UNIQUE_ID, Optional.fromNullable(uniqueID));
    }

    public EntityPlayer getOwner() {
        try {
            UUID uuid = this.getOwnerID();
            return uuid == null ? null : this.world.getPlayerEntityByUUID(uuid);
        } catch (IllegalArgumentException var2) {
            return null;
        }
    }

    public boolean isOwner(Entity entityIn) {
        return entityIn instanceof EntityPlayer && entityIn == this.getOwner();
    }

    public EntityPlayer getControllingPlayer() {
        Entity entity = this.getControllingPassenger();
        if (entity instanceof EntityPlayer) return (EntityPlayer) entity;
        return null;
    }

    @Override
    public void putAwaySword(EntityPlayer player) {
        if (!this.world.isRemote) {
            ItemStack sword = getItemStack();
            if (!player.addItemStackToInventory(sword)) this.entityDropItem(sword, 0);
        }
        this.world.removeEntity(this);
    }

    @SideOnly(Side.CLIENT)
    protected void updateClientControls() {
        Minecraft minecraft = Minecraft.getMinecraft();
        if (this.isRidingPlayer(minecraft.player)) {
            this.up(minecraft.gameSettings.keyBindJump.isKeyDown());
            this.down(ModKeys.FLYING_INSTRUMENT_DOWN.isKeyDown());
        }
    }

    private boolean up() {
        return (this.dataManager.get(CONTROL_STATE) & 1) == 1;
    }

    private boolean down() {
        return (this.dataManager.get(CONTROL_STATE) >> 1 & 1) == 1;
    }

    private void up(boolean up) {
        this.setStateField(0, up);
    }

    private void down(boolean down) {
        this.setStateField(1, down);
    }

    private void setStateField(int i, boolean newState) {
        byte prevState = this.dataManager.get(CONTROL_STATE);
        if (newState) this.dataManager.set(CONTROL_STATE, (byte) (prevState | (1 << i)));
        else this.dataManager.set(CONTROL_STATE, (byte) (prevState & ~(1 << i)));
    }

    private boolean isRidingPlayer(EntityPlayer player) {
        return this.isBeingRidden() && this.getControllingPassenger() == player;
    }
}
